package commonAdmin.dao;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import commonAdmin.domain.Page;
import commonAdmin.domain.WebPageSearchCondition;
import commonAdmin.sql.support.FieldColumnMapper;
import commonAdmin.sql.support.IdKey;
import commonAdmin.sql.support.ModelTableInfo;
import commonAdmin.sql.support.SqlStatmentWithArgs;
import commonAdmin.util.ObjectUtil;

/**
 * TODO:dao实例初始化时生成select,update,insert,delete语句,不用每次调用方法都去生成
 *@author Jack
 *2016年2月19日下午10:13:39
 */
public class BaseDaoImpl<Domain, ID> implements BaseDao<Domain, ID> {

	private static final Logger log = LoggerFactory.getLogger(BaseDaoImpl.class);

	@Autowired
	protected NamedParameterJdbcTemplate namedParamTemplate;

	@Autowired
	protected JdbcTemplate jdbcTemplate;

	protected ModelTableInfo modelInfo;
	protected String tableName;
	protected String selectSegment;
	protected Class<?> modelKlass;

	public BaseDaoImpl() {
		Type[] pts = ObjectUtil.getParameterizedType(this.getClass());
		if (pts.length > 0) {
			modelKlass = (Class<?>) pts[0];
			modelInfo = new ModelTableInfo(modelKlass);
			this.tableName = modelInfo.getTableName();
			this.selectSegment = buildSelectSegment();
		}
	}

	/**
	 * 构建select columnName as fieldName,省去维护RowMapper
	 * @return
	 */
	private String buildSelectSegment() {
		StringBuilder select = new StringBuilder("select ");
		List<FieldColumnMapper> fields = new ArrayList<FieldColumnMapper>(modelInfo.getFields().size());
		fields.addAll(modelInfo.getFields());
		fields.add(modelInfo.getIdKey());
		//modelInfo.getFields();
		for (FieldColumnMapper field : fields) {
			if (field.getColumnName().equals(field.getFieldName())) {
				select.append(field.getColumnName()).append(",");
			} else {
				select.append(field.getColumnName()).append(" as ").append(field.getFieldName()).append(",");
			}
		}
		if (modelInfo.getFields().size() > 0)
			select.delete(select.length() - 1, select.length());
		return select.toString();
	}

	@Override
	public ID add(Domain domain) {
		IdKey idKey = modelInfo.getIdKey();
		ID id = (ID) idKey.get(domain);
		StringBuilder insert = new StringBuilder().append(" insert into ").append(modelInfo.getTableName()).append("(");
		StringBuilder values = new StringBuilder().append(" values(");
		if (id != null) {//如果id不为空,则insert语句中包含id列
			insert.append(idKey.getColumnName());
			values.append(":").append(idKey.getFieldName()).append(",");
		}
		for (FieldColumnMapper field : modelInfo.getFields()) {
			insert.append(field.getColumnName()).append(",");
			values.append(":").append(field.getFieldName()).append(",");
		}
		if (modelInfo.getFields().size() > 0) {
			insert.replace(insert.length() - 1, insert.length(), ")");
			values.replace(values.length() - 1, values.length(), ")");
		}
		String sql = insert.append(" ").append(values).toString();
		if (id != null) {
			namedParamTemplate.update(sql, new BeanPropertySqlParameterSource(domain));
			return id;
		} else {
			KeyHolder keyHolder = new GeneratedKeyHolder();
			namedParamTemplate.update(sql, new BeanPropertySqlParameterSource(domain), keyHolder);
			System.err.println(keyHolder.getKey().getClass());//Long
			if(idKey.getField().getType() == Integer.class){
				return (ID) new Integer(keyHolder.getKey().intValue());
			}
			return (ID) keyHolder.getKey();
		}
	}

	@Override
	public boolean delete(ID id) {
		String sql = "delete from " + tableName + " where id=?";
		int affectRows = jdbcTemplate.update(sql, id);
		if (affectRows == 0)
			log.info("delete affect rows is 0!sql is {},id is{}", sql, id);
		return true;
	}

	@Override
	public boolean update(Domain domain) {
		StringBuilder update = new StringBuilder();
		update.append("update ").append(modelInfo.getTableName()).append(" set ");
		for (FieldColumnMapper field : modelInfo.getFields()) {
			update.append(field.getColumnName()).append("=:").append(field.getFieldName());
			update.append(",");
		}
		if (modelInfo.getFields().size() > 0) {
			update.delete(update.length() - 1, update.length());
		}
		IdKey idKey = modelInfo.getIdKey();
		update.append(" where ").append(idKey.getColumnName()).append("=:").append(idKey.getFieldName());
		int affectedRows = namedParamTemplate.update(update.toString(), new BeanPropertySqlParameterSource(domain));
		if (affectedRows == 0)
			log.info(" update affected rows is 0! sql is {} and values is {}", update.toString(), domain);
		return true;
	}

	@Override
	public Domain get(ID id) {
		String sql = selectSegment + " from " + tableName + " where id = ?";
		//		Object obj = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(modelKlass), id);
		//		
		//		return obj!=null ? (Domain)obj:null;
		return (Domain) jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(modelKlass), id);
	}

	@Override
	public Page<Domain> queryForPage(WebPageSearchCondition<Domain> searchCondition, Page<Domain> page) {
		SqlStatmentWithArgs whereCondition = buildPageQueryConditionStatement(searchCondition, page);
		if (whereCondition == null)
			whereCondition = new SqlStatmentWithArgs(StringUtils.EMPTY, new ArrayList(0));
		String sql = this.selectSegment + " from " + tableName + whereCondition.getSql() + " limit ?,?";
		String countSql = "select count(*) from " + tableName + whereCondition.getSql();
		
		List list = new ArrayList(whereCondition.getArgs().size());
		list.addAll(whereCondition.getArgs());
		int start = (page.getPageNum() - 1) * page.getPageSize();
		list.add(start);
		list.add(page.getPageSize());
		
		logStatement(countSql, whereCondition.getArgs());
		logStatement(sql, list);
		
		Integer totalRecords = jdbcTemplate.queryForObject(countSql,whereCondition.getArgs().toArray(), Integer.class);
		List<Domain> domains = jdbcTemplate.query(sql, list.toArray(), new BeanPropertyRowMapper(modelKlass));
		
		page.setTotalItem(totalRecords);//总页数
		page.setItems(domains);//
		return page;
	}

	/**
	 * 构建分页查询条件子句,子类覆盖实现
	 * @param searchCondition
	 * @param page
	 * @return where statement as:<strong>where name=a and age=22</strong> group by col1 order by col1
	 */
	protected SqlStatmentWithArgs buildPageQueryConditionStatement(WebPageSearchCondition<Domain> searchCondition, Page<Domain> page) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 打印sql
	 * @param statement
	 * @param args
	 */
	public void logStatement(String statement, Object... args) {
		StringBuilder argsBuilder = new StringBuilder("(");
		for (Object arg : args) {
			argsBuilder.append(arg + ",");
		}
		if (argsBuilder.length() > 1) {
			argsBuilder.deleteCharAt(argsBuilder.length() - 1);
		}
		argsBuilder.append(")");
		log.info(" >>> " + statement + " >>> " + argsBuilder);
	}
}
